
  function Router( config ){
    window.onload = Router.init.bind(this, config);
    window.onhashchange = Router.init.bind(this, config);
  }

  Router.init = function( config ){
    !location.hash.includes('#') && (location.hash = '#/');

    var hash = location.hash;
    var path = hash.match(/#([^?]+)\??/i)[1];
    var route = null;

    $$('[view]').forEach(function(div){
      route = config.routes.find(function(item){
        return item.path === path;
      });
      div.innerHTML = route.page( parseURL(hash), route.meta || {} ) || '';
    });

    if(config.headTitleEl){
      var title = route.meta.title || route.path;
      document.title = title;
      var headTitleEl = $SELECT(config.headTitleEl);
      headTitleEl.innerHTML = title;
      if(route.meta.hasBack){
        var a = document.createElement('a');
        a.className = 'icon-arrow-left';
        a.onclick = function(){
          history.back();
        }
        headTitleEl.prepend(a);
      }

    }
    

    $$('[link]').forEach(function(a){
      var match = a.getAttribute('href') === hash;
      a.classList[match?'add':'remove']('active');
      config.change && config.change(a,match);
    })

  }

  //函数工具集合

  function $ID(id){
    return document.getElementById(id);
  }

  function $SELECT(expr){
    return document.querySelector(expr);
  }

  function $$(selector){
    return document.querySelectorAll(selector);
  }

  // 根据字符内容生成唯一哈希码
  function hashCode(str) {
    var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-'.split('');
    var hash = 5381;
    var i = str.length - 1;
    if (typeof str == 'string') {
      for (; i > -1; i--) hash += (hash << 5) + str.charCodeAt(i);
    } else {
      for (; i > -1; i--) hash += (hash << 5) + str[i];
    }
    var value = hash & 0x7FFFFFFF;
    var code = '';
    do {
      code += chars[value & 0x3F];
    }
    while (value >>= 6);
    return code;
  }

  // 首字母大写命名
  function firstUpperCase( name ){
    return name.slice(0,1).toUpperCase() + name.slice(1);
  }

  // 驼峰法命名
  function camelCase( name ){
    return name.toLowerCase().replace(/_([a-z])/g,function($,$1){
      return $1.toUpperCase();
    })
  }

  // 帕斯卡命名
  function pascalCase( name ){
    return firstUpperCase(camelCase( name ));
  }

  // 将url查询字符串转换为对象
  function parseURL( url ) {
    var params = {};
    if(!url.includes('?')) return params;
    var urls = url.split('?');
    
    var arr = urls[1].split('&') ;
    for (var i = 0, len = arr.length; i < len; i++) {
      var a = arr[i].split('=');
      params[a[0]] = a[1];
    }
    return params;
  }

  // 抽取url中文件名（不含扩展名）
  function pickName( url ){
    return url.match(/\/(\w+)\.[a-z]+$/)[1];
  }
  
  // 抽取模板中部分代码内容
  function pickCode(html, tag='script'){
    var codes = [];
    var reg = new RegExp('<'+tag+'[^>]*src="([^"]+)"|<'+tag+'>([\\s\\S]*?)<\\/'+tag+'>','igm');
    do {
      var match = reg.exec(html);
      if (match) {
        if (match[1]) {
          codes.push({id: hashCode(match[1]), url: match[1]});
        } else if (match[2]) {
          codes.push({id: hashCode(match[2]), code: match[2]});
        }
      }
    } while (reg.lastIndex);
    return codes;
  }

  // ajax异步导入
  function importAsync( url ){
    return function( params, meta ){

      var xhr = new XMLHttpRequest();
      xhr.onload = function(){
        var html = xhr.responseText;
        var filename = pickName(url);

        //封装插入模板
        var htmls = pickCode(html,'template').reduce(function(code, item){
          return code + item.code;
        },'');

        $$('[view]').forEach(function(div){
          div.innerHTML = htmls;
        })

        pickCode(html,'style').forEach(function(item){
          if(item.code){
            var id = hashCode(item.code);
            if(!$ID(id)){
              var style = document.createElement('style');
              style.id = id;
              style.textContent = item.code;
              document.head.append(style);
            }
          }
        })

        pickCode(html,'script').forEach(function(item){
          var script = document.createElement('script');
          var id = hashCode(item.url || item.code);
          if(!$ID(id)){
            script.id = id;
            if(item.url){
              script.src = item.url;
            }else{
              script.textContent = item.code;
            }
            document.body.append(script);
          }

        })

        var callback = pascalCase(filename);
        typeof window[callback] === 'function' && window[callback](params, meta);
       
      }
      xhr.open('GET', url );
      xhr.send();
    }
  }